home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / SCSI Samples 1.0 / SCSI Find Devices 06⁄15 ƒ / Src / SCSIFindNextDevice.c < prev   
Encoding:
C/C++ Source or Header  |  1994-06-16  |  30.7 KB  |  986 lines  |  [TEXT/KAHL]

  1. /*                                SCSIFindNextDevice.c                            */
  2. /*
  3.  * SCSIFindNextDevice.c
  4.  * Copyright © 1992-94 Apple Computer Inc. All Rights Reserved.
  5.  *
  6.  * Find all SCSI devices. The alogrithm first asks the SCSI Manager for the
  7.  * number of buses, then loops through each bus for each device and LUN.
  8.  * old SCSI Manager. This is made complex by the flexible SCSI Manager 4.3
  9.  * architecture: it is possible for the asynchronous SCSI Manager to only
  10.  * be available on a third-party bus interface, for example. Because of this,
  11.  * we must always scan the bus using the original SCSI Manager even if the
  12.  * asynchronous manager is present.
  13.  */
  14. #include <Errors.h>
  15. #include <Events.h>
  16. #include <Memory.h>
  17. #include <OSUtils.h>
  18. #include <Traps.h>
  19. #include <Types.h>
  20. #include "SCSIFindDevices.h"
  21. #ifndef FALSE
  22. #define FALSE    0
  23. #define TRUE    1
  24. #endif
  25.  
  26. /*
  27.  * All functions use the same formal parameter to access the "global" record.
  28.  */
  29. #define REC    (*scsiFindDevicesPtr)
  30.  
  31. /*
  32.  * These are the states that control the overall process.
  33.  */
  34. enum {
  35.     kStateInitialize = 0,
  36.     kStateNextBus,
  37.     kStateNextTarget,
  38.     kStateNextLUN,
  39.     kStateCheckForHardWired,
  40.     kStateNextHardWiredTarget,
  41.     kStateNextHardWiredLUN,
  42.     kStateLastStateWithoutATrailingCommaBecauseWeCareAboutYou
  43. };
  44.  
  45. /*
  46.  * These are the commands that may be sent to the device. Request Sense is only
  47.  * sent by the original SCSI Manager.
  48.  */
  49. #define kScsiCmdInquiry                0x12
  50. #define kScsiCmdRequestSense        0x03
  51.  
  52. /*
  53.  * These are the device types that SCSI knows about.
  54.  */    
  55. enum {
  56.     kScsiDevTypeDirect                    = 0,
  57.     kScsiDevTypeSequential,
  58.     kScsiDevTypePrinter,
  59.     kScsiDevTypeProcessor,
  60.     kScsiDevTypeWorm,                        /* Write-once, read multiple        */
  61.     kScsiDevTypeCDROM,
  62.     kScsiDevTypeScanner,
  63.     kScsiDevTypeOptical,
  64.     kScsiDevTypeChanger,
  65.     kScsiDevTypeComm,
  66.     kScsiDevTypeGraphicArts0A,
  67.     kScsiDevTypeGraphicArts0B,
  68.     kScsiDevTypeFirstReserved,                /* Start of reserved sequence        */
  69.     kScsiDevTypeUnknownOrMissing        = 0x1F,
  70.     kScsiDevTypeMask                    = 0x1F
  71. };
  72. /*
  73.  * These are device type modifiers. We need them to distinguish between "unknown"
  74.  * and "missing" devices.
  75.  */
  76. enum {
  77.     kScsiDevTypeQualifierConnected        = 0x00,    /* Exists and is connected        */
  78.     kScsiDevTypeQualifierNotConnected    = 0x20,    /* Logical unit exists            */
  79.     kScsiDevTypeQualifierReserved        = 0x40,
  80.     kScsiDevTypeQualifierMissing        = 0x60,    /* No such logical unit            */
  81.     kScsiDevTypeQualifierVendorSpecific    = 0x80,    /* Other bits are unspecified    */
  82.     kScsiDevTypeQualifierMask            = 0xE0
  83. };
  84. #define kScsiDevTypeMissing    \
  85.     (kScsiDevTypeUnknownOrMissing | kScsiDevTypeQualifierMissing)
  86. /*
  87.  * This is the data that is returned after a GetExtendedStatus request. The
  88.  * errorCode gives a general indication of the error, which may be qualified by
  89.  * the additionalSenseCode and additionalSenseQualifier fields. These may be
  90.  * device (vendor) specific values, however. The info[] field contains additional
  91.  * information. For a media error, it contains the failing logical block number
  92.  * (most-significant byte first).
  93.  */
  94. struct SCSI_Sense_Data {                    /* Request Sense result                */
  95.     unsigned char        errorCode;            /*  0    Class code, valid lbn        */
  96.     unsigned char        segmentNumber;        /*  1    Segment number                */
  97.     unsigned char        senseKey;            /*  2    Sense key and flags            */
  98.     unsigned char        info[4];
  99.     unsigned char        additionalSenseLength;
  100.     unsigned char        reservedForCopy[4];
  101.     unsigned char        additionalSenseCode;
  102.     unsigned char        additionalSenseQualifier;    
  103.     unsigned char        fruCode;            /* Field replacable unit code        */
  104.     unsigned char        senseKeySpecific[2];
  105.     unsigned char        additional[101];
  106. };
  107. typedef struct SCSI_Sense_Data SCSI_Sense_Data;
  108. /*
  109.  * The high-bit of errorCode signals whether there is a logical
  110.  * block. The low value signals whether there is a valid sense
  111.  */
  112. #define kScsiSenseHasLBN            0x80    /* Logical block number set            */
  113. #define kScsiSenseInfoValid            0x70    /* Is sense key valid?                */
  114. #define kScsiSenseInfoMask            0x70    /* Mask for sense info                */
  115. /*
  116.  * These bits may be set in the sense key
  117.  */
  118. #define kScsiSenseKeyMask            0x0F
  119. #define kScsiSenseILI                0x20    /* Illegal logical Length            */
  120. #define kScsiSenseEOM                0x40    /* End of media                        */
  121. #define kScsiSenseFileMark            0x80    /* End of file mark                    */
  122.  
  123. /*
  124.  * SCSI sense codes. (Returned after request sense).
  125.  */
  126. #define     kScsiSenseNone                0x00    /* No error                            */
  127. #define     kScsiSenseRecoveredErr        0x01    /* Warning                            */
  128. #define     kScsiSenseNotReady            0x02    /* Device not ready                    */
  129. #define     kScsiSenseMediumErr        0x03    /* Device medium error                */
  130. #define     kScsiSenseHardwareErr        0x04    /* Device hardware error            */
  131. #define     kScsiSenseIllegalReq        0x05    /* Illegal request for dev.            */
  132. #define     kScsiSenseUnitAtn            0x06    /* Unit attention (not err)            */
  133. #define     kScsiSenseDataProtect        0x07    /* Data protection                    */
  134. #define     kScsiSenseBlankCheck        0x08    /* Tape-specific error                */
  135. #define     kScsiSenseVendorSpecific    0x09    /* Vendor-specific error            */
  136. #define     kScsiSenseCopyAborted        0x0a    /* Copy request cancelled            */
  137. #define     kScsiSenseAbortedCmd        0x0b    /* Initiator aborted cmd.            */
  138. #define     kScsiSenseEqual            0x0c    /* Comparison equal                    */
  139. #define     kScsiSenseVolumeOverflow    0x0d    /* Write past end mark                */
  140. #define     kScsiSenseMiscompare        0x0e    /* Comparison failed                */
  141. #define     kScsiSenseCurrentErr        0x70
  142. #define     kScsiSenseDeferredErr        0x71
  143.  
  144.  
  145. /*
  146.  * SCSI command status (from status phase)
  147.  */
  148. #define     kScsiStatusGood            0x00    /* Normal completion                */
  149. #define     kScsiStatusCheckCondition    0x02    /* Need GetExtendedStatus            */
  150. #define     kScsiStatusConditionMet    0x04    /* For Compare Command?                */
  151. #define     kScsiStatusBusy            0x08    /* Device busy (self-test?)            */
  152. #define     kScsiStatusIntermediate    0x10    /* Intermediate status                */
  153. #define     kScsiStatusResConflict        0x18    /* Reservation conflict                */
  154. #define     kScsiStatusQueueFull        0x28    /* Target can't do command            */
  155. #define     kScsiStatusReservedMask    0x3e    /* Vendor specific?                    */
  156.  
  157. /*
  158.  * This is the maximum number of times we try to grab the SCSI Bus
  159.  */
  160. #define kMaxSCSIRetries                40        /* 10 seconds, 4 times/sec            */
  161. /*
  162.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  163.  * if either the BSY or SEL bit is set).
  164.  */
  165. #ifndef kScsiStatBSY
  166. #define kScsiStatBSY                (1 << 6)
  167. #endif
  168. #ifndef kScsiStatSEL
  169. #define kScsiStatSEL                (1 << 1)
  170. #endif
  171. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  172.  
  173. /*
  174.  * These private routines do all the work.
  175.  */
  176. static void                    CheckForAsyncSCSI(
  177.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  178.     );
  179. static void                    GetHighHostBusAdaptor(
  180.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  181.     );
  182. static OSErr                SetupForNextSCSIBus(
  183.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  184.     );
  185. static Boolean                IsRegisteredAsynchDevice(
  186.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  187.     );
  188. static Boolean                CheckForDevice(
  189.         register SCSIFindDevicesPtr    scsiFindDevicesPtr,
  190.         Boolean                    useAsyncManager
  191.     );
  192. static OSErr                OriginalSCSI(
  193.         unsigned short            targetID,
  194.         Ptr                        command,
  195.         Ptr                        resultData,
  196.         unsigned long            resultSize,
  197.         long                    *actualTransferSize
  198.     );
  199. static void                    ClearMemory(
  200.         void                    *recordPtr,
  201.         unsigned long            recordLength
  202.     );
  203. #define CLEAR(record)        ClearMemory(&record, sizeof record);
  204.  
  205.  
  206. /*
  207.  * This is the function that does all the work. Each time it is called, it finds
  208.  * the next device (next logical unit, next target, next bus). The overall design
  209.  * uses a state machine that continues within the function until one of three
  210.  * things happen: it finds a device, it reaches the end of the device sequence,
  211.  * or it gets an error.
  212.  */
  213. OSErr
  214. SCSIFindNextDevice(
  215.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  216.     )
  217. {
  218.         OSErr                        status;
  219.         Boolean                        doNextState;
  220.         
  221.         if (REC.deviceID.bus == 0xFF)
  222.             REC.state = kStateInitialize;
  223.         /*
  224.          * This is the overall state machine that processes application calls.
  225.          * Code in this section only organizes the lower-level routines. Many of
  226.          * the tests within the state switch change state. In all cases, they set
  227.          * doNextState TRUE and exit the switch. This, eventually, continues at
  228.          * the while statement which re-runs the state switch. A state-machine
  229.          * organization is not necessarily the best (and goto's would be slightly
  230.          * more efficient), but looping through a common switch statement
  231.          * simplified debugging.
  232.          */
  233.         doNextState = TRUE;
  234.         while (doNextState) {
  235.             doNextState = FALSE;
  236.             switch (REC.state) {
  237.             case kStateInitialize:
  238.                 /*
  239.                  * Initialization: check for the presence of the asynchronous
  240.                  * SCSI Manager and get the last host bus. Then start with the
  241.                  * first bus.
  242.                  */
  243.                 REC.scsiExecIOPB = NULL;
  244.                 REC.execIOPBSize = 0;
  245.                 CheckForAsyncSCSI(scsiFindDevicesPtr);
  246.                 GetHighHostBusAdaptor(scsiFindDevicesPtr);
  247.                 REC.state = kStateNextBus;
  248.                 doNextState = TRUE;
  249.                 break;
  250.             case kStateNextBus:
  251.                 /*
  252.                  * Look for the first/next bus. If we have a bus, we do some
  253.                  * bus-specific initializations including, primarily, creating
  254.                  * the SCSI parameter block.
  255.                  */
  256.                 if (REC.deviceID.bus == 0xFF)
  257.                     REC.deviceID.bus = 0;            /* Do the first bus            */
  258.                 else {
  259.                     ++REC.deviceID.bus;                /* Do the next bus            */
  260.                 }
  261.                 if (REC.deviceID.bus <= REC.lastHostBus) {
  262.                     /*
  263.                      * We have another bus to test. Make a SCSI parameter block
  264.                      * that is properly sized for this bus.
  265.                      */
  266.                     status = SetupForNextSCSIBus(scsiFindDevicesPtr);
  267.                     if (status == noErr) {
  268.                         /*
  269.                          * This bus exists: check its targets.
  270.                          */
  271.                         REC.deviceID.targetID = 0xFF;
  272.                         REC.state = kStateNextTarget;
  273.                         doNextState = TRUE;
  274.                     }
  275.                     else if (status == eofErr) {    /* eofErr is private        */
  276.                         /*
  277.                          * This bus does not exist. There may be gaps in the bus
  278.                          * sequence if a third-party SCSI adaptor installs its
  279.                          * own, private, SCSI Manager 4.3 on a machine that does
  280.                          * not otherwise support asynchronous SCSI. Just continue
  281.                          * with the next bus until we reach REC.lastHostBus.
  282.                          */
  283.                         REC.state = kStateNextBus;
  284.                         doNextState = TRUE;
  285.                     }
  286.                     else {
  287.                         /*
  288.                          * Oops: this is a serious error. Since doNextState is
  289.                          * FALSE, we will exit the while loop and return to the
  290.                          * caller with a serious error.
  291.                          */
  292.                     }
  293.                 }
  294.                 else {
  295.                     /*
  296.                      * We've examined all of the buses accessable through SCSI
  297.                      * Manager 4.3. Check for third-party devices that are only
  298.                      * accessable through the original SCSI Manager (because they
  299.                      * patch the SCSI Manager traps).
  300.                      */
  301.                     REC.deviceID.targetID = 0xFF;
  302.                     REC.state = kStateCheckForHardWired;
  303.                     doNextState = TRUE;
  304.                 }
  305.                 break;
  306.             case kStateNextTarget:
  307.                 /*
  308.                  * Look for the next device on this bus. If we run off the end,
  309.                  * cycle back through the switch to look for the next bus.
  310.                  */
  311.                 if (REC.deviceID.targetID == 0xFF)
  312.                     REC.deviceID.targetID = 0;            /* Do the first target    */
  313.                 else {
  314.                     ++REC.deviceID.targetID;            /* Do the next target    */
  315.                 }
  316.                 /*
  317.                  * REC.initiatorID is set on a bus-by-bus basis. We cannot assume
  318.                  * that it is always equal to seven. Skip over the initiator.
  319.                  * REC.maxTargetID is normally seven; but it's provided to us
  320.                  * by the SCSI Manager, so we'll use that value.
  321.                  */ 
  322.                 if (REC.deviceID.targetID == REC.initiatorID)
  323.                     ++REC.deviceID.targetID;
  324.                 if (REC.deviceID.targetID > REC.maxTargetID) {
  325.                     /*
  326.                      * We've done all targets for this bus. Go on to the next bus.
  327.                      */
  328.                     REC.state = kStateNextBus;
  329.                 }
  330.                 else {
  331.                     /*
  332.                      * New target: do the first logical unit for this bus/target.
  333.                      */
  334.                     REC.deviceID.LUN = 0xFF;
  335.                     REC.state = kStateNextLUN;
  336.                 }
  337.                 doNextState = TRUE;
  338.                 break;
  339.             case kStateNextLUN:
  340.                 /*
  341.                  * We have a host bus and target ID. Cycle through the logical
  342.                  * units for this target ID. We will always look at LUN zero.
  343.                  * When we reach the end, the switch will take us to the next
  344.                  * target ID.
  345.                  */
  346.                 if (REC.deviceID.LUN == 0xFF)
  347.                     REC.deviceID.LUN = 0;                /* Do the first LUN        */
  348.                 else {
  349.                     ++REC.deviceID.LUN;                    /* Do the next LUN        */
  350.                 }
  351.                 if (REC.deviceID.LUN > REC.maxBusLUN) {
  352.                     REC.state = kStateNextTarget;
  353.                     doNextState = TRUE;
  354.                 }
  355.                 else {
  356.                     /*
  357.                      * Look for this LUN. Failures look for the next target.
  358.                      */
  359.                     if (CheckForDevice(scsiFindDevicesPtr, REC.useAsynchSCSI))
  360.                          status = noErr;                /* This one exists        */
  361.                     else {
  362.                         /*
  363.                          * This target/LUN was not found. Go on to the next
  364.                          * SCSI bus ID. Note that we presume that there are no
  365.                          * gaps in the LUN sequence.
  366.                          */
  367.                         REC.state = kStateNextTarget;
  368.                         doNextState = TRUE;
  369.                     }
  370.                 }
  371.                 break;
  372.             case kStateCheckForHardWired:
  373.                 /*
  374.                  * We have found all devices that can be accessed through the
  375.                  * asynchronous SCSI Manager. Some third-party hardware interfaces
  376.                  * do not use the asynchronous SCSI Manager, but patch original
  377.                  * SCSI traps. If the asynchronous SCSI Manager is not available,
  378.                  * we don't have to continue here (the above code found all
  379.                  * devices. In this segment, we must hard-wire the initiator ID
  380.                  * to seven, as there is no supported way to determine it from the
  381.                  * SCSI Manager or operating system.
  382.                  */
  383.                 if (REC.isAsyncSCSIPresent == FALSE)
  384.                     status = eofErr;                /* All done, thank you        */
  385.                 else {
  386.                     REC.deviceID.bus = 0;            /* Set the hard-wired data    */
  387.                     REC.initiatorID = 7;
  388.                     REC.maxTargetID = 7;
  389.                     REC.state = kStateNextHardWiredTarget;
  390.                     REC.deviceID.targetID = 0xFF;
  391.                     doNextState = TRUE;
  392.                 }
  393.                 break;
  394.             case kStateNextHardWiredTarget:
  395.                 /*
  396.                  * Look at the next target on the built-in bus.
  397.                  */
  398.                 if (REC.deviceID.targetID == 0xFF)
  399.                     REC.deviceID.targetID = 0;
  400.                 else {
  401.                     ++REC.deviceID.targetID;
  402.                 }
  403.                 if (REC.deviceID.targetID == REC.initiatorID)
  404.                     ++REC.deviceID.targetID;
  405.                 if (REC.deviceID.targetID >= REC.maxTargetID)
  406.                     status = eofErr;                /* All done, thank you        */
  407.                 else {
  408.                     REC.deviceID.LUN = 0xFF;
  409.                     REC.state = kStateNextHardWiredLUN;
  410.                     doNextState = TRUE;
  411.                 }
  412.                 break;
  413.             case kStateNextHardWiredLUN:
  414.                 /*
  415.                  * Look for the next LUN on the built-in bus.
  416.                  */
  417.                 if (REC.deviceID.LUN == 0xFF) {
  418.                     REC.maxBusLUN = REC.maxLUN;
  419.                     REC.deviceID.LUN = 0;                /* Do the first LUN        */
  420.                 }
  421.                 else {
  422.                     ++REC.deviceID.LUN;                    /* Do the next LUN        */
  423.                 }
  424.                 if (REC.deviceID.LUN > REC.maxBusLUN) {
  425.                     REC.state = kStateNextHardWiredTarget;
  426.                     doNextState = TRUE;
  427.                 }
  428.                 else {
  429.                     /*
  430.                      * Look for this LUN. failures look for the next target.
  431.                      * IsRegisteredAsynchDevice is TRUE if this device did not
  432.                      * register with the asynchronous SCSI Manager.
  433.                      */
  434.                     if (IsRegisteredAsynchDevice(scsiFindDevicesPtr)) {
  435.                         /*
  436.                          * Since we know about this device, it would have been
  437.                          * found by the first pass through the asynchronous
  438.                          * SCSI Manager. So, we don't need to look at it here.
  439.                          */
  440.                         REC.state = kStateNextHardWiredLUN;
  441.                         doNextState = TRUE;
  442.                     }
  443.                     else if (CheckForDevice(scsiFindDevicesPtr, FALSE)) {
  444.                         /*
  445.                          * This device was not registered, but it does exist
  446.                          * if we check using the original SCSI Manager. Probably
  447.                          * because someone patched the SCSI Manager traps.
  448.                          * Return this to the caller.
  449.                          */
  450.                          status = noErr;                /* This one exists        */
  451.                     }
  452.                     else {
  453.                         /*
  454.                          * This target/LUN was not found. Go on to the next
  455.                          * SCSI bus ID. Note that we presume that there are no
  456.                          * gaps in the LUN sequence.
  457.                          */
  458.                         REC.state = kStateNextHardWiredTarget;
  459.                         doNextState = TRUE;
  460.                     }
  461.                 }
  462.                 break;
  463.             default:                                    /* Illegal state        */
  464.                 status = abortErr;                        /* Can't happen            */
  465.                 break;
  466.             }                                            /* State switch            */
  467.         }                                                /* While doNextState    */
  468.         if (status != noErr) {
  469.             /*
  470.              * We've done everything that we can. Re-initialize and release
  471.              * our saved parameter block.
  472.              */
  473.             REC.deviceID.bus = 0xFF;                    /* Force reinitialize    */
  474.             if (REC.scsiExecIOPB != NULL) {
  475.                 DisposePtr((Ptr) REC.scsiExecIOPB);
  476.                 REC.scsiExecIOPB = NULL;
  477.             }
  478.         }
  479.         return (status);
  480. }
  481.  
  482. /*
  483.  * Check whether the Asynchronous SCSI trap is available.
  484.  * Set REC.isAsyncSCSI appropriately.
  485.  */
  486. static void
  487. CheckForAsyncSCSI(
  488.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  489.     )
  490. {
  491.         TrapType                trapType;
  492.         short                    theTrap;
  493.  
  494. /*
  495.  * TrapAvailable (see Inside Mac VI 3-8)
  496.  */
  497. #define NumToolboxTraps() (                                \
  498.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  499.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  500.             ? 0x200 : 0x400                                \
  501.     )
  502. #define GetTrapType(theTrap) (                            \
  503.         (((theTrap) & 0x0800) != 0) ? ToolTrap : OSTrap    \
  504.     )
  505.         theTrap = _SCSIAtomic;
  506.         trapType = GetTrapType(theTrap);
  507.         if (trapType == ToolTrap) {
  508.             theTrap &= 0x07FF;
  509.             if (theTrap >= NumToolboxTraps())
  510.                 theTrap = _Unimplemented;
  511.         }
  512.         REC.isAsyncSCSIPresent = (
  513.                 NGetTrapAddress(theTrap, trapType)
  514.                 != NGetTrapAddress(_Unimplemented, ToolTrap)
  515.             );
  516. #undef NumToolboxTraps
  517. #undef GetTrapType
  518. }
  519.  
  520. /*
  521.  * If we have the asynchronous SCSI Manager, find out how many buses are present
  522.  * on this system. If we're limited to the original SCSI Manager, force a "single
  523.  * bus" scan, since the function that actually calls the SCSI Manager ignores the
  524.  * bus if the asynchronous manager is not present.
  525.  */
  526. static void
  527. GetHighHostBusAdaptor(
  528.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  529.     )
  530. {
  531.         OSErr                            status;
  532.         SCSIBusInquiryPB                busInquiryPB;
  533. #define PB                                (busInquiryPB)
  534.  
  535.         if (REC.isAsyncSCSIPresent == FALSE)
  536.             REC.lastHostBus = 0;
  537.         else {
  538.             CLEAR(PB);
  539.             PB.scsiPBLength = sizeof PB;
  540.             PB.scsiFunctionCode = SCSIBusInquiry;
  541.             PB.scsiDevice.bus = 0xFF;
  542.             status = SCSIAction((SCSI_PB *) &PB);
  543.             REC.lastHostBus = PB.scsiHiBusID;
  544.         }
  545. #undef PB
  546. }
  547.  
  548. /*
  549.  * Start to check a new SCSI bus. If we find a bus, allocate (or re-allocate)
  550.  * the SCSIExecIO command block. Note that it is possible to have buses with
  551.  * no devices, and gaps in the bus sequence. For example, in a Macintosh with
  552.  * two buses (such as the Quadra 950 or PowerMac 8100), you can have a bus
  553.  * with no devices.Also, if you install a third-party bus adaptor that supports
  554.  * the asynchronous SCSI Manager on a machine with two buses, it would be
  555.  * assigned bus 2 (with buses 0 and 1 referencing the internal system buses).
  556.  * In this case, a system could have no devices on bus 0 or 1.
  557.  */ 
  558. static OSErr
  559. SetupForNextSCSIBus(
  560.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  561.     )
  562. {
  563.         OSErr                        status;
  564.         SCSIBusInquiryPB            busInquiryPB;
  565. #define PB                            (busInquiryPB)
  566.  
  567.         /*
  568.          * If we don't support asynchronous SCSI, nothing happens here.
  569.          */
  570.         if (REC.isAsyncSCSIPresent == FALSE) {
  571.             REC.useAsynchSCSI = FALSE;
  572.             REC.initiatorID = 7;
  573.             REC.maxTargetID = 7;
  574.             REC.maxBusLUN = REC.maxLUN;
  575.             status = noErr;
  576.         }
  577.         else {
  578.             CLEAR(PB);
  579.             PB.scsiPBLength = sizeof PB;
  580.             PB.scsiFunctionCode = SCSIBusInquiry;
  581.             PB.scsiDevice.bus = REC.deviceID.bus;    /* Other values are zero    */
  582.             status = SCSIAction((SCSI_PB *) &PB);
  583.             if (status == noErr) {
  584.                 REC.useAsynchSCSI = TRUE;            /* Asynch works on this bus    */
  585.                 REC.initiatorID = PB.scsiInitiatorID;
  586.                 REC.maxTargetID = PB.scsiMaxTarget;
  587.                 /*
  588.                  * If we are running on a Quadra 840-AV with a CD300, the
  589.                  * Macintosh will hang if it tries to access LUN 1. We check for
  590.                  * this problem in two ways: by examining a global maxLUN flag,
  591.                  * and by checking whether the bug was fixed, either by running
  592.                  * on later hardware or by installing a System Update.
  593.                  */
  594.                 REC.enableATN =
  595.                     (busInquiryPB.scsiWeirdStuff & scsiTargetDrivenSDTRSafe) != 0; 
  596.                 REC.maxBusLUN = (REC.enableATN) ? REC.maxLUN : 0;
  597.                 /*
  598.                  * Allocate a parameter block for this request using the size that
  599.                  * was returned in the busInquiry parameter block.
  600.                  */
  601.                 if (REC.execIOPBSize != PB.scsiIOpbSize) {
  602.                     if (REC.scsiExecIOPB != NULL) {
  603.                         DisposePtr((Ptr) REC.scsiExecIOPB);
  604.                         REC.scsiExecIOPB = NULL;
  605.                     }
  606.                     REC.scsiExecIOPB =
  607.                                 (SCSIExecIOPB *) NewPtrClear(PB.scsiIOpbSize);
  608.                     if (REC.scsiExecIOPB == NULL)
  609.                         status = MemError();
  610.                     else {
  611.                         REC.execIOPBSize = PB.scsiIOpbSize;
  612.                         status = noErr;
  613.                     }
  614.                 }
  615.             }
  616.             else if (PB.scsiDevice.bus == 0) {
  617.                 /*
  618.                  * If bus zero is not registered, it must be accessed via the
  619.                  * original API. Set the initiatorID to the default 7 - there is
  620.                  * no supported mechanism for determining the actual id (it's
  621.                  * hidden inside the parameter RAM, but cannot be set by a
  622.                  * published mechanism).
  623.                  */
  624.                 REC.useAsynchSCSI = FALSE;
  625.                 REC.initiatorID = 7;
  626.                 REC.maxTargetID = 7;
  627.                 REC.maxBusLUN = REC.maxLUN;
  628.                 status = noErr;
  629.             }
  630.             else {
  631.                 /*
  632.                  * This is a problem: this bus cannot be accessed unless it has
  633.                  * been registered. For example, the second bus of a Quadra
  634.                  * 950 cannot be accessed if the SCSI Manager extension is not
  635.                  * installed and a third-party asynchronous SCSI Manager card
  636.                  * is installed as bus 2. Return a private error to skip this bus.
  637.                  */
  638.                 status = eofErr;
  639.             }
  640.         }
  641.         return (status);
  642. #undef PB
  643. }
  644.  
  645. /*
  646.  * This is called if the asynchronous SCSI Manager is present to sweep up any
  647.  * third party devices that did not register with the asynchronous SCSI Manager.
  648.  */
  649. static Boolean
  650. IsRegisteredAsynchDevice(
  651.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  652.     )
  653. {
  654.         OSErr                            status;
  655.         SCSIGetVirtualIDInfoPB            scsiGetVirtualIDInfo;
  656.  
  657.          CLEAR(scsiGetVirtualIDInfo);
  658.          scsiGetVirtualIDInfo.scsiPBLength = sizeof scsiGetVirtualIDInfo;
  659.          scsiGetVirtualIDInfo.scsiOldCallID = REC.deviceID.targetID;
  660.          status = SCSIAction((SCSI_PB *) &scsiGetVirtualIDInfo);
  661.          return (status == noErr);
  662. }
  663.  
  664. /*
  665.  * This is the only function that sends SCSI commands to a device. It will send a
  666.  * Device Inquiry and, if Check Condition is returned, issue Request Sense.
  667.  */
  668. static Boolean
  669. CheckForDevice(
  670.         register SCSIFindDevicesPtr    scsiFindDevicesPtr,
  671.         Boolean                    useAsyncManager
  672.     )
  673. {
  674.  
  675.         OSErr                    status;
  676.         OSErr                    requestSenseStatus;
  677.         SCSI_Sense_Data            senseData;
  678. #define SENSE    (senseData)
  679.         Boolean                    result;
  680.         /*
  681.          * For the old SCSI Manager only.
  682.          */
  683.         unsigned char            command[6];
  684.         long                    tempLong;
  685.         
  686.         if (useAsyncManager) {
  687. #define PB    (*REC.scsiExecIOPB)
  688.             /*
  689.              * Setup the parameter block for the user's request.
  690.              */
  691.             PB.scsiPBLength = REC.execIOPBSize;
  692.             PB.scsiFunctionCode = SCSIExecIO;
  693.             PB.scsiTimeout = 1000;
  694.             PB.scsiDevice = REC.deviceID;
  695.             PB.scsiCDBLength = 6;
  696.             PB.scsiCDB.cdbBytes[0] = kScsiCmdInquiry;
  697.             PB.scsiCDB.cdbBytes[4] = sizeof REC.inquiry;
  698.             /*
  699.              * Stuff the LUN into the command for SCSI-1 devices.
  700.              */
  701.             /* PB.scsiCDB.cdbBytes[1] &= ~0xE0; -- already zero */
  702.             PB.scsiCDB.cdbBytes[1] |= (REC.deviceID.LUN & 0x03) << 5;
  703.             /*
  704.              * Specify the transfer direction, if any, and setup the other SCSI
  705.              * operation flags. scsiSIMQNoFreeze prevents the SCSI Manager from
  706.              * blocking further operation if an error is detected.
  707.              */
  708.             PB.scsiFlags = scsiTransferPolled;
  709.             PB.scsiDataPtr = (unsigned char *) &REC.inquiry;
  710.             PB.scsiDataLength = sizeof REC.inquiry;
  711.             PB.scsiDataType = scsiDataBuffer;
  712.             PB.scsiSensePtr = (unsigned char *) &senseData;
  713.             PB.scsiSenseLength = sizeof senseData;
  714.             PB.scsiFlags = scsiSIMQNoFreeze | scsiDirectionIn | scsiDontDisconnect;
  715.             if (REC.enableATN == FALSE)
  716.                 PB.scsiIOFlags |= scsiDisableSelectWAtn;
  717.             status = SCSIAction((SCSI_PB *) &PB);
  718.             if (status == noErr)
  719.                 status = PB.scsiResult;
  720.             /*
  721.              * Note: scsiDataRunError is issued if our transfer request was larger
  722.              * or smaller than the actual transfer length. We need to examine the
  723.              * actual transfer sizes to see how to handle this error. This is
  724.              * not necessarily complete or correct. The intent here is to supress
  725.              * the transfer length error when executing Device Inquiry or other
  726.              * administrative commands with variable-length result blocks.
  727.              */
  728.             REC.actualInquirySize = PB.scsiDataLength - PB.scsiDataResidual;
  729.             if (status == scsiDataRunError                /* Over/underrun error    */
  730.              && REC.actualInquirySize > 0)                /* And its a short read    */
  731.                 status = noErr;                            /* If so, ignore error    */            
  732.             /*
  733.              * If the device issued Check Condition and the SCSI Manager was able
  734.              * to retrieve a Request Sense datum, change the error to our private
  735.              * "Check Condition" status.
  736.              */
  737.             if (status == scsiNonZeroStatus
  738.              && (PB.scsiResultFlags & scsiAutosenseValid) != 0) {
  739.                  status = statusErr;
  740.                  requestSenseStatus = noErr;
  741.             }
  742. #undef PB
  743.         }
  744.         else {
  745.             CLEAR(command);
  746.             command[0] = kScsiCmdInquiry;
  747.             command[4] = sizeof REC.inquiry;
  748.             /*
  749.              * Stuff the LUN into the command.
  750.              */
  751.             /* command[1] &= ~0xE0;    -- already zero */
  752.             command[1] |= (REC.deviceID.LUN & 0x03) << 5;
  753.             status = OriginalSCSI(
  754.                         REC.deviceID.targetID,
  755.                         (Ptr) &command,
  756.                         (Ptr) &REC.inquiry,
  757.                         sizeof REC.inquiry,
  758.                         &REC.actualInquirySize
  759.                     );
  760.             if (status == statusErr) {
  761.                 /*
  762.                  * Check condition
  763.                  */
  764.                 CLEAR(command);
  765.                 command[0] = kScsiCmdRequestSense;
  766.                 command[4] = sizeof senseData;
  767.                 /*
  768.                  * Stuff the LUN into the command.
  769.                  */
  770.                 /* command[1] &= ~0xE0;    -- already zero */
  771.                 command[1] |= (REC.deviceID.LUN & 0x03) << 5;
  772.                 requestSenseStatus = OriginalSCSI(
  773.                             REC.deviceID.targetID,
  774.                             (Ptr) &command,
  775.                             (Ptr) &senseData,
  776.                             sizeof senseData,
  777.                             &tempLong
  778.                         );
  779.                 if (status == noErr)
  780.                     status = statusErr;
  781.             }
  782.         }
  783.         /*
  784.          * Look at the result.
  785.          */
  786.         switch (status) {
  787.         case noErr:
  788.             if (REC.inquiry.devType == kScsiDevTypeMissing)
  789.                 result = FALSE;
  790.             else {
  791.                 result = TRUE;                    /* Normal successful return        */
  792.             }
  793.             break;
  794.         case statusErr:
  795.             /*
  796.              * The target returned Check Condition. We need to look at the sense
  797.              * data, if any, to distinguish between an "offline" but present device,
  798.              * and a non-existant logical unit. Note: some drives return Check
  799.              * Condition, and "no sense error" if we try to access an incorrect
  800.              * logical unit. This might reasonably be remapped as "illegal request.
  801.              */
  802.             if (requestSenseStatus != noErr
  803.              || (SENSE.errorCode & kScsiSenseInfoMask) != kScsiSenseInfoValid)
  804.                  result = FALSE;
  805.              else {
  806.                 switch (SENSE.senseKey & kScsiSenseKeyMask) {
  807.                 case kScsiSenseIllegalReq:
  808.                     result = FALSE;
  809.                     break;
  810.                 default:
  811.                     /*
  812.                      * Wierd: some drives seem to set the End of Media bit
  813.                      * in the sense key if an invalid LUN is selected.
  814.                      */
  815.                     if ((SENSE.senseKey & kScsiSenseEOM) != 0)
  816.                         result = FALSE;
  817.                     else {
  818.                         result = TRUE;
  819.                     }
  820.                     break;
  821.                 }
  822.             }
  823.             break;    
  824.         default:                            /* Other errors == no such device    */
  825.             result = FALSE;
  826.             break;
  827.         }
  828.         return (result);
  829. }
  830.  
  831. /*
  832.  * This is a very limited wrapper for the original SCSI Manager that can handle
  833.  * Device Inquiry and Request Sense (only).
  834.  */
  835. static OSErr
  836. OriginalSCSI(
  837.         unsigned short            targetID,
  838.         Ptr                        command,
  839.         Ptr                        resultData,
  840.         unsigned long            resultSize,
  841.         long                    *actualTransferSize
  842.     )
  843. {
  844.         OSErr                    status;
  845.         OSErr                    completionStatus;
  846.         short                    totalTries;            /* Get/Select retries        */
  847.         short                    getTries;            /* Get retries                */
  848.         short                    iCount;                /* Bus free counter            */
  849.         unsigned long            watchdog;            /* Timeout after this        */
  850.         SCSIInstr                tib[4];
  851.         short                    messageByte;
  852.         short                    statusByte;
  853.  
  854.         *actualTransferSize = 0;
  855.         tib[0].scOpcode = scInc;
  856.         tib[0].scParam1 = (unsigned long) resultData;
  857.         tib[0].scParam2 = 1;
  858.         tib[1].scOpcode = scAdd;
  859.         tib[1].scParam1 = (unsigned long) actualTransferSize;
  860.         tib[1].scParam2 = 1;
  861.         tib[2].scOpcode = scLoop;
  862.         tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
  863.         tib[2].scParam2 = resultSize;
  864.         tib[3].scOpcode = scStop;
  865.         tib[3].scParam1 = 0;
  866.         tib[3].scParam2 = 0;
  867.         /*
  868.          * Arbitrate for the scsi bus.  This will fail if some other device is
  869.          * accessing the bus at this time (which is unlikely).
  870.          *
  871.          *** Do not set breakpoints or call any functions that may require device
  872.          *** I/O (such as display code that accesses font resources between
  873.          *** SCSIGet and SCSIComplete,
  874.          *
  875.          */
  876.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  877.             for (getTries = 0; getTries < 4; getTries++) {
  878.                 /*
  879.                  * Wait for the bus to go free.
  880.                  */
  881.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  882.                 while (ScsiBusBusy()) {
  883.                     if (TickCount() > watchdog) {
  884.                         status = scArbNBErr;
  885.                         goto exit;
  886.                     }
  887.                 }
  888.                 /*
  889.                  * The bus is free, try to grab it
  890.                  */
  891.                 for (iCount = 0; iCount < 4; iCount++) {
  892.                     if ((status = SCSIGet()) == noErr)
  893.                         break;
  894.                 }
  895.                 if (status == noErr)
  896.                     break;                            /* Success: we have the bus */
  897.                 /*
  898.                  * The bus became busy again. Try to wait for it to go free.
  899.                  */
  900.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  901.                     ;
  902.             } /* The getTries loop */
  903.             if (status != noErr) {
  904.                 /*
  905.                  * The SCSI Manager thinks the bus is not busy and not selected,
  906.                  * but "someone" has set its internal semaphore that signals
  907.                  * that the SCSI Manager itself is busy. The application will have
  908.                  * to handle this problem. (We tried getTries * 4 times).
  909.                  */
  910.                 goto exit;
  911.             }
  912.             /*
  913.              * We now own the SCSI bus. Try to select the device.
  914.              */
  915.             if ((status = SCSISelect(targetID)) != noErr)
  916.                 goto exit;
  917.             /*
  918.              * From this point on, we must exit through SCSIComplete() even if an
  919.              * error is detected. Send a command to the selected device. There are
  920.              * several failure modes, including an illegal command (such as a
  921.              * write to a read-only device). If the command failed because of
  922.              * "device busy", we will try it again.
  923.              */
  924.             status = SCSICmd((Ptr) command, 6);
  925.             if (status == noErr)
  926.                 status = SCSIRead((Ptr) tib);
  927.             /*
  928.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  929.              * returning the status and command-completion message bytes..
  930.              */
  931.             completionStatus = SCSIComplete(&statusByte, &messageByte, 60L);
  932.             /*
  933.              * If we have an error here, return as the "final" status.
  934.              * 
  935.              */
  936.             if (completionStatus != noErr)
  937.                 status = completionStatus;
  938.             else {
  939.                 /*
  940.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  941.                  * second and try again.
  942.                  */
  943.                 if (statusByte == kScsiStatusBusy) {
  944.                     watchdog = TickCount() + 15;
  945.                     while (TickCount() < watchdog)
  946.                         ;
  947.                     continue;                /* Do next totalTries attempt        */
  948.                 }
  949.             }
  950.             /*
  951.              * This is the normal exit (success) or final failure exit.
  952.              */
  953.             break;
  954.         } /* totalTries loop */
  955. exit:    /*
  956.          * Ignore phase errors if the buffer was large enough
  957.          */
  958.         if (status == scPhaseErr && *actualTransferSize <= resultSize)
  959.             status = noErr;
  960.         /*
  961.          * Return statusErr if the device returns Check Condition:
  962.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  963.          * that may cause the real SCSI Status Byte to be in the Message byte.
  964.          */
  965.         if (statusByte == kScsiStatusGood
  966.          && messageByte == kScsiStatusCheckCondition)
  967.             statusByte = kScsiStatusCheckCondition;
  968.         if (status == noErr && statusByte == kScsiStatusCheckCondition)
  969.             status = statusErr;
  970.         return (status);
  971. }
  972.  
  973.  
  974. static void
  975. ClearMemory(
  976.         void                    *recordPtr,
  977.         register unsigned long    recordLength
  978.     )
  979. {
  980.         register char            *ptr;
  981.         
  982.         for (ptr = (char *) recordPtr; recordLength > 0; --recordLength)
  983.             *ptr++ = 0; 
  984. }
  985.  
  986.